#pragma once
#include <unordered_map>
#include <string>
#include <iostream>
#include <sys/epoll.h>
#include <cstdlib>

class Event;
class Reactor;
typedef int (*callback_t)(Event *ev);

//每个fd都需要有自己的专属的输入输出缓冲区
class Event //该节点需要epoll管理
{
public:
    int sock;
    std::string inbuffer;
    std::string outbuffer;

    //通过回调方式来进行，等和拷贝上的代码逻辑解耦
    //该sock文件描述符的各个IO事件的回调函数
    callback_t recver;
    callback_t sender;
    callback_t errer;
    //设置Event回指Reactor指针
    Reactor *R;

    Event() : sock(-1),
              recver(0),
              sender(0),
              errer(0),
              R(0)
    {
    }
    //注册回调函数
    void RegisterCallBack(callback_t _recver, callback_t _sender, callback_t _errer)
    {
        recver = _recver;
        sender = _sender;
        errer = _errer;
    }
    ~Event() {}
};

//该类不关心sock的类型(不管listen_sock,还是读，还是写)
//只关心如何使用该类管理Event节点
class Reactor
{
private:
    int epfd;
    //一但底层有文件描述符符号就绪，就可以通过fd在map找到对应的事件，调用其回调函数处理IO事件
    std::unordered_map<int, Event *> events; //文件描述符，和该文件描述符对应的事件
public:
    Reactor() : epfd(-1) {}
    ~Reactor() {}

    void InitReactor()
    {
        epfd = epoll_create(128);
        if (epfd < 0)
        {
            std::cerr << "Error: epoll_create failed" << std::endl;
            exit(1);
        }
        std::cout << "Epoll created successfully" << std::endl;
    }
    bool InsertEvent(Event *evp, uint32_t evs) // evs是evp所关心的事件
    {
        // 1.将sock插入到epoll模型中
        struct epoll_event ev;
        ev.events = evs;
        ev.data.fd = evp->sock;

        if (epoll_ctl(epfd, EPOLL_CTL_ADD, evp->sock, &ev) < 0)
        {
            std::cerr << "epoll_ctl failed" << std::endl;
            return false;
        }

        // 2.将Event插入到map中
        events.insert(std::make_pair(evp->sock, evp));
        return true;
    }
    void DeleteEvent(Event *evp)
    {
        int sock = evp->sock;
        auto iter = events.find(sock);
        if (iter != events.end())
        {
            epoll_ctl(epfd, EPOLL_CTL_DEL, sock, nullptr);

            events.erase(iter);

            close(sock);

            delete evp;
        }
    }
    //修改事件的读写：使能读写接口
    bool EableRW(int sock, bool eb_r, bool eb_w)
    {
        struct epoll_event ev;
        ev.events = EPOLLET | (eb_r ? EPOLLIN : 0) | (eb_w ? EPOLLOUT : 0);
        ev.data.fd = sock;
        if (epoll_ctl(epfd, EPOLL_CTL_MOD, sock, &ev) < 0)
        {
            std::cerr << "epoll_ctl failed" << std::endl;
            return false;
        }
        return true;
    }
    bool IsSockOk(int sock)
    {
        auto it = events.find(sock);
        return it != events.end();
    }
    //就绪事件派发器
    /**
     * 做到了就绪事件和IO事件的解耦
     * 通过调用就绪事件文件描述符的回调函数去完成
     *
     * @param timeout
     */
    void Dispastcher(int timeout = -1) //默认阻塞
    {
        struct epoll_event revs[64];
        int n = epoll_wait(epfd, revs, 64, timeout);

        for (int i = 0; i < n; i++)
        {
            int sock = revs[i].data.fd;
            uint32_t revent = revs[i].events;

            //差错处理，如果该文件描述符有错误事件，和对端关闭连接事件
            //都转化为到对应IO函数中处理该错误事件
            if (revent & EPOLLERR)
                revent |= (EPOLLIN | EPOLLOUT);
            if (revent & EPOLLHUP)
                revent |= (EPOLLIN | EPOLLOUT);

            //读事件就绪
            if (revent & EPOLLIN)
            {
                //注意这里处理：不再是调用read函数，accpet函数这些去处理读事件就绪
                //而是调用其回到函数，统一处理读事件

                //通过sock找到在map对应读事件
                if (IsSockOk(sock) && events[sock]->recver)
                    //调用读事件的回调方法
                    events[sock]->recver(events[sock]);
            }
            if (revent & EPOLLOUT)
            {
                //找到sock对应的写事件回调函数
                if (IsSockOk(sock) && events[sock]->sender)
                    events[sock]->sender(events[sock]);
            }
        }
    }
};